#include "Logger.h"

#include <QFile>
#include <QDir>
#include <QTextStream>
#include <QDateTime>
#include <QApplication>

#include <stdio.h>

#define LOG_TYPE_DEBUG 0
#define LOG_TYPE_INFO 1
#define LOG_TYPE_WARNING 2
#define LOG_TYPE_ERROR 3

////////////////////////////

#define DIR "./logs/"
#define DIR_WIN "logs\\"
#define DIR_MAC "/Documents/%1/logs/"

////////////////////////////////////////////// INSTANCE ///

Logger *Logger::mInstance = nullptr;

void Logger::enable()
{
    if (nullptr == Logger::mInstance) {
        Logger::mInstance = new Logger();
    }

    Logger::mInstance->openLog();
}

void Logger::enableWithLogToFile()
{
    if (nullptr == Logger::mInstance) {
        Logger::mInstance = new Logger();
    }

    Logger::mInstance->openLogToFile();
}

void Logger::disable()
{
    if (nullptr != Logger::mInstance) {
        Logger::mInstance->closeLog();
    }
}

void Logger::release()
{
    if (nullptr != Logger::mInstance) {
        Logger::mInstance->closeLog();

        delete Logger::mInstance;
        Logger::mInstance = nullptr;
    }
}

///===========================================================

void Logger::d(const QString &tag, const QString &message)
{
    if (nullptr != mInstance && mInstance->isLogging()) {
        LogData *data = new LogData();
        data->tag = tag;
        data->msg = message;

        Logger::mInstance->onLogComming(LOG_TYPE_DEBUG, data);
    }
}

void Logger::i(const QString &tag, const QString &message)
{
    if (nullptr != mInstance && mInstance->isLogging()) {
        LogData *data = new LogData();
        data->tag = tag;
        data->msg = message;

        Logger::mInstance->onLogComming(LOG_TYPE_WARNING, data);
    }
}

void Logger::w(const QString &tag, const QString &message)
{
    if (nullptr != mInstance && mInstance->isLogging()) {
        LogData *data = new LogData();
        data->tag = tag;
        data->msg = message;

        Logger::mInstance->onLogComming(LOG_TYPE_WARNING, data);
    }
}

void Logger::e(const QString &tag, const QString &message)
{
    if (nullptr != mInstance && mInstance->isLogging()) {
        LogData *data = new LogData();
        data->tag = tag;
        data->msg = message;

        Logger::mInstance->onLogComming(LOG_TYPE_ERROR, data);
    }
}


///////////////////////////////////////////////// LOGGER ///
Logger::Logger(QObject *parent) : QThread(parent)
  , mIsRunning(false), mIsLogToFile(false)
  , m_curReadIndex(0), m_curWriteIndex(0)
{

}

Logger::~Logger()
{

}

void Logger::openLog()
{
    mIsLogToFile = false;

    if (QThread::isRunning()) {
        return;
    }

    mIsRunning = true;
    QThread::start();
}

void Logger::openLogToFile()
{
    mIsLogToFile = true;

    if (QThread::isRunning()) {
        return;
    }

    mIsRunning = true;
    QThread::start();
}

void Logger::closeLog()
{
    if (!mIsRunning) {
        return;
    }

    mIsRunning = false;

    do {
        this->msleep(50);
    } while (QThread::isRunning() && !QThread::isFinished());
}

void Logger::onLogComming(int type, void *data)
{
    LogData *log = static_cast<LogData*>(data);
    if (nullptr != log) {
        QString message = "[" + log->tag + "]" + log->msg;
        QString date = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss.zzz") + " ";
        switch (type) {
        case LOG_TYPE_DEBUG:
            message = date + "[D]" + message;
            break;
        case LOG_TYPE_INFO:
            message = date + "[I]" + message;
            break;
        case LOG_TYPE_WARNING:
            message = date + "[W]" + message;
            break;
        case LOG_TYPE_ERROR:
            message = date + "[E]" + message;
            break;
        default:
            break;
        }

        //... append log to message queue
        appendLog(message);

        //... release
        delete data;
    }
}

void Logger::appendLog(const QString &message)
{
    if (mIsLogToFile) {
        m_mutexLock.lock();
        m_messageList[m_curWriteIndex].append(message);
        m_mutexLock.unlock();

    } else {
        fprintf(stderr, "%s\n", message.toUtf8().data());

    }
}

//---------------------------
void Logger::run()
{
    fprintf(stderr, "Logger thread started...\n");

    if (mIsLogToFile) {
        QDir dir;
        QString dir_path;
        QString date = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh");

#ifdef Q_OS_MAC
        dir_path = QDir::homePath() + QString(DIR_MAC).arg(qApp->organizationName()+"/"+qApp->applicationName().replace(" ", "-"));
#elif (defined Q_OS_WIN)
        dir_path = DIR_WIN;
#else
        dir_path = DIR;
#endif
        dir.mkpath(dir_path);
        QFile file(QString("%1%2.log").arg(dir_path).arg(date));
        if (!file.open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Append)) {
            fprintf(stderr, " create log file failed!!!!!\n");
        }
        QTextStream out(&file);
        out.setCodec("UTF-8");

        do {
            if (m_mutexLock.tryLock(2)) {
                m_curReadIndex = m_curWriteIndex ? 0 : 1;
                foreach (QString msg, m_messageList[m_curReadIndex]) {
                    fprintf(stderr, "%s\n", msg.toUtf8().data());

                    //... write log to file
                    out << msg << endl;
                }
                out.flush();
                m_messageList[m_curReadIndex].clear();

                m_curWriteIndex = m_curWriteIndex ? 0 : 1;
                m_mutexLock.unlock();
            } else {
                fprintf(stderr, "------------------------------- { try lock failed }\n");
            }

            this->usleep(100);
        } while (mIsRunning || !m_messageList[0].isEmpty() || !m_messageList[1].isEmpty());

        file.close();
    } else {
        do {
            this->msleep(250);
        } while (mIsRunning);
    }

    fprintf(stderr, "Logger thread finished...\n");
}

